Tutustu frontend micro-frontend -tapahtumaväylän arkkitehtuuriin ja toteutukseen saumattoman sovellusten välisen viestinnän mahdollistamiseksi.
Sovellusten välisen viestinnän hallinta: Frontend Micro-Frontend Event Bus
Nykyaikaisessa web-kehityksessä micro-frontendit ovat nousseet voimakkaaksi arkkitehtuurimalliksi. Ne antavat tiimeille mahdollisuuden rakentaa ja ottaa käyttöön itsenäisiä käyttöliittymän osia, mikä edistää ketteryyttä, skaalautuvuutta ja tiimien autonomiaa. Kriittinen haaste syntyy kuitenkin, kun näiden itsenäisten sovellusten on kommunikoitava keskenään. Ilman vankkaa mekanismia micro-frontendit voivat jäädä eristyneiksi saarekkeiksi, mikä heikentää käyttäjien odottamaa yhtenäistä käyttäjäkokemusta. Tässä kohtaa Frontend Micro-Frontend Event Bus (tapahtumaväylä) astuu kuvaan, toimien sovellusten välisen viestinnän keskushermostona.
Micro-frontend-ympäristön ymmärtäminen
Ennen kuin syvennymme tapahtumaväylään, kerrataan lyhyesti micro-frontendien konteksti. Kuvittele suuri verkkokauppa-alusta. Yhden monoliittisen frontend-sovelluksen sijaan meillä voisi olla:
- Tuotekatalogin Micro-Frontend: Vastaa tuotelistauksien, haun ja suodatuksen näyttämisestä.
- Ostoskorin Micro-Frontend: Hallinnoi ostoskoriin lisättyjä tuotteita, määriä ja kassalle siirtymistä.
- Käyttäjäprofiilin Micro-Frontend: Hoitaa käyttäjän tunnistautumisen, tilaushistorian ja henkilötiedot.
- Suosittelumoottorin Micro-Frontend: Ehdottaa samankaltaisia tuotteita käyttäjän käyttäytymisen perusteella.
Jokainen näistä voidaan kehittää, ottaa käyttöön ja ylläpitää itsenäisesti eri tiimien toimesta. Tämä tarjoaa merkittäviä etuja:
- Teknologinen monimuotoisuus: Tiimit voivat valita parhaan teknologiastackin omaan micro-frontendiinsä.
- Tiimien autonomia: Kehitystiimit voivat työskennellä itsenäisesti ilman laajaa koordinointia.
- Nopeammat käyttöönottosyklit: Pienemmät, itsenäiset käyttöönotot vähentävät riskiä ja lisäävät nopeutta.
- Skaalautuvuus: Yksittäisiä micro-frontend-sovelluksia voidaan skaalata kysynnän mukaan.
Haaste: Sovellusten välinen viestintä
Itsenäisen kehityksen kauneuteen liittyy merkittävä haaste: miten nämä erilliset sovellukset keskustelevat keskenään? Harkitse näitä yleisiä skenaarioita:
- Kun käyttäjä lisää tuotteen ostoskoriin, tuotekatalogin saattaa olla tarpeen ilmoittaa visuaalisesti, että tuote on nyt korissa (esim. valintamerkillä).
- Kun käyttäjä kirjautuu sisään käyttäjäprofiilin kautta, muiden micro-frontendien (kuten suosittelumoottorin) on ehkä tiedettävä käyttäjän tunnistautumisen tila personoidakseen sisältöä.
- Kun käyttäjä tekee ostoksen, ostoskorin on ehkä ilmoitettava tuotekatalogille varastosaldon päivittämisestä tai käyttäjäprofiilille uuden tilaushistorian heijastamisesta.
Suoraa viestintää micro-frontendien välillä ei usein suositella, koska se luo tiukkaa kytkentää, mikä kumoaa monia micro-frontend-arkkitehtuurin etuja. Tarvitsemme löyhästi kytketyn, joustavan ja skaalautuvan tavan niiden vuorovaikutukselle.
Esittelyssä Frontend Micro-Frontend Event Bus
Tapahtumaväylä (event bus), joka tunnetaan myös nimillä viestiväylä (message bus) tai pub/sub (publish-subscribe) -järjestelmä, on suunnittelumalli, joka mahdollistaa irrallisen viestinnän sovelluksen eri osien välillä. Micro-frontendien kontekstissa se toimii keskuspaikkana, jossa sovellukset voivat julkaista tapahtumia ja muut sovellukset voivat tilata näitä tapahtumia.
Perusidea on yksinkertainen:
- Julkaisija (Publisher): Sovellus, joka luo tapahtuman ja lähettää sen väylään.
- Tilaaja (Subscriber): Sovellus, joka kuuntelee tiettyjä tapahtumia väylässä ja reagoi niiden tapahtuessa.
- Tapahtumaväylä (Event Bus): Välittäjä, joka helpottaa julkaistujen tapahtumien toimittamista kaikille kiinnostuneille tilaajille.
Tämä malli on myös läheistä sukua tarkkailija-suunnittelumallille (Observer pattern), jossa yksi objekti (kohde) ylläpitää listaa riippuvaisistaan (tarkkailijoista) ja ilmoittaa heille automaattisesti kaikista tilamuutoksista, tyypillisesti kutsumalla yhtä niiden metodeista.
Micro-frontendien tapahtumaväylän keskeiset periaatteet
- Irrotettu kytkentä (Decoupling): Julkaisijoiden ja tilaajien ei tarvitse tietää toistensa olemassaolosta. Ne ovat vuorovaikutuksessa vain tapahtumaväylän kautta.
- Asynkroninen viestintä: Tapahtumat käsitellään tyypillisesti asynkronisesti, mikä tarkoittaa, että julkaisijan ei tarvitse odottaa tilaajien lopettavan tapahtuman käsittelyä.
- Skaalautuvuus: Kun uusia micro-frontend-sovelluksia lisätään, ne voivat yksinkertaisesti tilata tai julkaista tapahtumia vaikuttamatta olemassa oleviin.
- Keskitetty logiikka (tapahtumille): Vaikka sovelluslogiikka pysyy hajautettuna, tapahtumien käsittelymekanismi on keskitetty väylän kautta.
Micro-frontend-tapahtumaväylän suunnittelu
Micro-frontend-tapahtumaväylän toteuttamiseen on useita lähestymistapoja, joilla kaikilla on omat etunsa ja haittansa. Valinta riippuu usein sovelluksesi erityistarpeista, käytetyistä taustateknologioista ja käyttöönottostrategiasta.
1. Globaali tapahtumien lähettäjä (JavaScript)
Tämä on yleinen ja suhteellisen suoraviivainen lähestymistapa micro-frontend-sovelluksille, jotka on otettu käyttöön samassa selainkontekstissa (esim. käyttämällä module federation -tekniikkaa tai iframe-viestintää). Yksi jaettu JavaScript-objekti toimii tapahtumaväylänä.
Toteutusesimerkki (käsitteellinen JavaScript)
Voimme luoda yksinkertaisen tapahtumien lähettäjäluokan:
class EventBus {
constructor() {
this.listeners = {};
}
subscribe(event, callback) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
return () => {
this.unsubscribe(event, callback);
};
}
unsubscribe(event, callback) {
if (!this.listeners[event]) {
return;
}
this.listeners[event] = this.listeners[event].filter(listener => listener !== callback);
}
publish(event, data) {
if (!this.listeners[event]) {
return;
}
this.listeners[event].forEach(callback => {
try {
callback(data);
} catch (error) {
console.error(`Error in event handler for ${event}:`, error);
}
});
}
}
// Pääsovelluksen kuoressa tai jaetussa apufunktiotiedostossa:
export const sharedEventBus = new EventBus();
Kuinka micro-frontendit käyttävät sitä
Tuotekatalogin Micro-Frontend (Julkaisija):
import { sharedEventBus } from './sharedEventBus'; // Olettaen, että sharedEventBus on tuotu oikein
function handleAddToCartButtonClick(productId) {
// ... logiikka tuotteen lisäämiseksi ostoskoriin ...
sharedEventBus.publish('itemAddedToCart', { productId: productId, quantity: 1 });
}
Ostoskorin Micro-Frontend (Tilaaja):
import { sharedEventBus } from './sharedEventBus'; // Olettaen, että sharedEventBus on tuotu oikein
// Kun ostoskorikomponentti liitetään tai alustetaan
const subscription = sharedEventBus.subscribe('itemAddedToCart', (eventData) => {
console.log('Tuote lisätty ostoskoriin:', eventData);
// Päivitä ostoskorin käyttöliittymä, lisää tuote sisäiseen tilaan jne.
updateCartUI(eventData.productId, eventData.quantity);
});
// Muista peruuttaa tilaus, kun komponentti poistetaan, muistivuotojen estämiseksi
// componentWillUnmount() { subscription(); }
Huomioitavaa globaaleista tapahtumien lähettäjistä
- Soveltamisala (Scope): Tämä lähestymistapa toimii hyvin, kun micro-frontendit ladataan samassa selainikkunassa ja ne jakavat globaalin soveltamisalan tai yhteisen moduulijärjestelmän (kuten Webpackin Module Federation).
- Muistivuodot: On erittäin tärkeää toteuttaa asianmukaiset tilauksen perumismekanismit, kun micro-frontend-komponentit poistetaan, jotta vältetään muistivuodot.
- Tapahtumien nimeämiskäytännöt: Määrittele selkeät nimeämiskäytännöt tapahtumille törmäysten estämiseksi ja ylläpidettävyyden varmistamiseksi. Käytä esimerkiksi etuliitettä, kuten
[micro-frontend-nimi]:tapahtumanNimi. - Tietorakenne: Määrittele yhdenmukaiset tietorakenteet tapahtumille.
2. Mukautetut tapahtumat ja DOM-lähetys
Toinen selainpohjainen lähestymistapa hyödyntää DOMia viestintäkanavana. Micro-frontendit voivat lähettää mukautettuja tapahtumia jaetulla DOM-elementillä (esim. window-objekti tai nimetty säilöelementti), ja muut micro-frontendit voivat kuunnella näitä tapahtumia.
Toteutusesimerkki (käsitteellinen JavaScript)
Tuotekatalogin Micro-Frontend (Julkaisija):
function handleAddToCartButtonClick(productId) {
const event = new CustomEvent('microfrontend:itemAddedToCart', {
detail: { productId: productId, quantity: 1 }
});
window.dispatchEvent(event);
}
Ostoskorin Micro-Frontend (Tilaaja):
const handleItemAdded = (event) => {
console.log('Tuote lisätty ostoskoriin:', event.detail);
updateCartUI(event.detail.productId, event.detail.quantity);
};
window.addEventListener('microfrontend:itemAddedToCart', handleItemAdded);
// Muista poistaa kuuntelija, kun komponentti poistetaan
// window.removeEventListener('microfrontend:itemAddedToCart', handleItemAdded);
Huomioitavaa mukautetuista tapahtumista
- Selainyhteensopivuus:
CustomEventon laajalti tuettu, mutta se on aina hyvä tarkistaa. - Tiedonsiirtorajoitukset:
CustomEvent-tapahtumandetail-ominaisuus voi siirtää mitä tahansa sarjallistettavaa dataa. - Globaalin nimiavaruuden saastuminen: Tapahtumien lähettäminen
window-objektille voi johtaa nimitörmäyksiin, jos sitä ei hallita huolellisesti. - Suorituskyky: Erittäin suurille tapahtumamäärille tämä ei ehkä ole suorituskykyisin ratkaisu verrattuna omistettuun tapahtumien lähettäjään.
3. Viestijonot tai ulkoiset välittäjät (monimutkaisempiin skenaarioihin)
Micro-frontend-sovelluksille, jotka saattavat toimia eri selainkonteksteissa (esim. iframe-kehykset eri alkuperistä), tai jos tarvitset vankempia ominaisuuksia, kuten taatun toimituksen, viestien pysyvyyden tai lähetyksen palvelinpuolen komponenteille, voit harkita ulkoisten viestijonojärjestelmien käyttöä.
Esimerkkejä ovat:
- WebSocketit: Reaaliaikaiseen, kaksisuuntaiseen viestintään.
- Server-Sent Events (SSE): Yksisuuntaiseen palvelimelta asiakkaalle suuntautuvaan viestintään.
- Omistetut viestinvälittäjät: Kuten RabbitMQ, Apache Kafka tai pilvipohjaiset ratkaisut (AWS SQS/SNS, Google Cloud Pub/Sub).
Toteutusesimerkki (käsitteellinen - WebSocketit)
Taustajärjestelmän WebSocket-palvelin toimii keskusvälittäjänä.
Tuotekatalogin Micro-Frontend (Julkaisija):
// Olettaen, että WebSocket-yhteys on luotu ja sitä hallitaan globaalisti
function handleAddToCartButtonClick(productId) {
if (websocketConnection.readyState === WebSocket.OPEN) {
websocketConnection.send(JSON.stringify({
event: 'itemAddedToCart',
data: { productId: productId, quantity: 1 }
}));
}
}
Ostoskorin Micro-Frontend (Tilaaja):
// Olettaen, että WebSocket-yhteys on luotu ja sitä hallitaan globaalisti
websocketConnection.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.event === 'itemAddedToCart') {
console.log('Tuote lisätty ostoskoriin (WS:stä):', message.data);
updateCartUI(message.data.productId, message.data.quantity);
}
};
Huomioitavaa ulkoisista välittäjistä
- Infrastruktuurin ylläpitokustannukset: Vaatii erillisen palvelun pystyttämistä ja hallintaa.
- Viive: Viestintä kulkee tyypillisesti palvelimen kautta, mikä voi aiheuttaa viivettä.
- Monimutkaisuus: Monimutkaisempi pystyttää ja hallita kuin selainpohjaiset ratkaisut.
- Skaalautuvuus ja luotettavuus: Tarjoaa usein paremman skaalautuvuuden ja luotettavuustakuut.
- Eri alkuperien välinen viestintä (Cross-Origin): Välttämätön eri alkuperistä tuleville iframe-kehyksille.
Parhaat käytännöt micro-frontend-tapahtumaväylän toteuttamiseen
Valitusta toteutustavasta riippumatta parhaiden käytäntöjen noudattaminen takaa vankan ja ylläpidettävän järjestelmän.
1. Määrittele selkeä sopimus tapahtumille
Jokaisella tapahtumalla tulisi olla selkeästi määritelty rakenne. Tämä sisältää:
- Tapahtuman nimi: Ainutlaatuinen ja kuvaava tunniste.
- Datan rakenne (Payload): Tapahtuman sisältämän datan muoto ja tyypit.
Esimerkki:
Tapahtuman nimi: userProfile:authenticated
Data:
{
"userId": "abc-123",
"timestamp": "2023-10-27T10:30:00Z"
}
2. Luo nimeämiskäytännöt
Nimiristiriitojen välttämiseksi, erityisesti suuremmissa micro-frontend-arkkitehtuureissa, ota käyttöön johdonmukainen nimeämisstrategia. Etuliitteet ovat erittäin suositeltavia.
- Sovellusaluepohjaiset etuliitteet:
[microfrontend-nimi]:[tapahtumanNimi](esim.catalog:productViewed,cart:itemRemoved) - Toimialuepohjaiset etuliitteet:
[toimialue]:[tapahtumanNimi](esim.auth:userLoggedIn,orders:orderPlaced)
3. Varmista asianmukainen tilauksen peruutus
Muistivuodot ovat yleinen sudenkuoppa. Varmista aina, että kuuntelijat poistetaan, kun ne rekisteröinyt komponentti tai micro-frontend ei ole enää aktiivinen. Tämä on erityisen kriittistä yksisivuisissa sovelluksissa (SPA), joissa komponentteja luodaan ja tuhotaan dynaamisesti.
// Esimerkki Reactin kaltaisella kehyksellä
import React, { useEffect } from 'react';
import { sharedEventBus } from './sharedEventBus';
function OrderSummary({ orderId }) {
useEffect(() => {
const subscription = sharedEventBus.subscribe('order:statusUpdated', (data) => {
if (data.orderId === orderId) {
console.log('Tilauksen tila päivitetty:', data.status);
// Päivitä komponentin tila uuden statuksen perusteella
}
});
// Siivousfunktio: peruuta tilaus, kun komponentti poistetaan
return () => {
subscription(); // Tämä kutsuu subscriben palauttamaa tilauksen peruutustoimintoa
};
}, [orderId]); // Tilaa uudelleen, jos orderId muuttuu
return (
Tilaus #{orderId}
{/* ... tilauksen tiedot ... */}
);
}
4. Käsittele virheet hallitusti
Mitä tapahtuu, jos tilaaja aiheuttaa virheen? Tapahtumaväylän toteutuksen ei tulisi mieluiten pysäyttää muiden tilaajien käsittelyä. Toteuta try...catch-lohkot takaisinkutsufunktioiden ympärille varmistaaksesi järjestelmän kestävyyden.
5. Harkitse tapahtumien rakeisuutta
Vältä luomasta liian laajoja tapahtumia, jotka lähettävät liikaa dataa tai liian usein. Toisaalta, älä luo liian spesifejä tapahtumia, jotka johtavat tapahtumatyyppien räjähdysmäiseen kasvuun.
- Liian laaja: Tapahtuma kuten
dataChangedon hyödytön. - Liian spesifi:
productNameChanged,productPriceChanged,productDescriptionChangedvoisi olla parempi jakaa yhdeksiproduct:updated-tapahtumaksi, jossa tietyt kentät ilmaisevat, mikä muuttui, tai antaa datan omistavan sovelluksen hoitaa ne.
Tavoittele tasapainoa, joka edustaa merkityksellisiä tilamuutoksia tai toimintoja järjestelmässäsi.
6. Tapahtumien versiointi
Kun micro-frontend-arkkitehtuurisi kehittyy, tapahtumarakenteet saattavat joutua muuttumaan. Harkitse versiointistrategiaa tapahtumillesi, erityisesti jos käytät ulkoisia viestinvälittäjiä tai jos seisokkiaika ei ole vaihtoehto päivitysten aikana.
7. Globaali tapahtumaväylä jaettuna riippuvuutena
Jos käytät jaettua JavaScript-tapahtumien lähettäjää, varmista, että se on todella jaettu kaikkien micro-frontendiesi kesken. Teknologiat, kuten Webpack Module Federation, tekevät tästä suoraviivaista sallimalla moduulien paljastamisen ja kuluttamisen globaalisti.
// webpack.config.js (isäntäsovelluksessa)
module.exports = {
//...
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
catalogApp: 'catalogApp@http://localhost:3001/remoteEntry.js',
cartApp: 'cartApp@http://localhost:3002/remoteEntry.js',
},
shared: {
'./src/sharedEventBus': {
singleton: true,
eager: true // Lataa välittömästi
}
}
})
]
};
// webpack.config.js (micro-frontendissä 'catalogApp')
module.exports = {
//...
plugins: [
new ModuleFederationPlugin({
name: 'catalogApp',
filename: 'remoteEntry.js',
exposes: {
'./CatalogApp': './src/bootstrap',
'./SharedEventBus': './src/sharedEventBus'
},
shared: {
'./src/sharedEventBus': {
singleton: true,
eager: true
}
}
})
]
};
Milloin tapahtumaväylää ei kannata käyttää
Vaikka tapahtumaväylä on tehokas, se ei ole ihmelääke kaikkiin viestintätarpeisiin. Se soveltuu parhaiten tapahtumien lähettämiseen ja sivuvaikutusten käsittelyyn. Se ei yleensä ole ihanteellinen malli:
- Suora pyyntö/vastaus: Jos micro-frontend A tarvitsee tietyn datan micro-frontend B:ltä ja sen on odotettava sitä dataa välittömästi, suora API-kutsu tai jaettu tilanhallintaratkaisu saattaa olla sopivampi kuin tapahtuman laukaiseminen ja vastauksen odottaminen.
- Monimutkainen tilanhallinta: Monimutkaisen jaetun sovellustilan hallintaan useiden micro-frontendien välillä omistettu tilanhallintakirjasto (mahdollisesti omalla tapahtuma- tai tilausmallillaan) saattaa olla sopivampi.
- Kriittiset synkroniset operaatiot: Jos vaaditaan välitöntä, synkronista koordinointia, tapahtumaväylän asynkroninen luonne voi olla haitta.
Vaihtoehtoiset viestintämallit micro-frontend-arkkitehtuurissa
On syytä huomata, että tapahtumaväylä on vain yksi työkalu micro-frontendien viestintätyökalupakissa. Muita malleja ovat:
- Jaettu tilanhallinta: Kirjastot, kuten Redux, Vuex tai Zustand, voidaan jakaa micro-frontendien kesken yhteisen tilan hallintaan.
- Propsit ja takaisinkutsut: Kun yksi micro-frontend on upotettu tai koostettu suoraan toisen sisään (esim. Webpack Module Federationilla), voidaan käyttää suoraa prop-välitystä ja takaisinkutsuja, vaikka tämä lisääkin kytkentää.
- Web-komponentit/mukautetut elementit: Voivat kapseloida toiminnallisuutta ja paljastaa mukautettuja tapahtumia ja ominaisuuksia viestintää varten.
- Reititys ja URL-parametrit: Tilan jakaminen URL-osoitteen kautta voi olla yksinkertainen, tilaton tapa kommunikoida.
Usein kattavan micro-frontend-arkkitehtuurin rakentamisessa käytetään näiden mallien yhdistelmää.
Globaalit esimerkit ja huomioitavat seikat
Kun rakennat micro-frontend-tapahtumaväylää globaalille yleisölle, ota huomioon nämä seikat:
- Aikavyöhykkeet: Varmista, että kaikki tapahtumien aikaleimadata on yleisesti ymmärretyssä muodossa (kuten ISO 8601 UTC-ajassa) ja että vastaanottajat osaavat tulkita sen.
- Lokalisointi/kansainvälistäminen (i18n): Tapahtumat itse eivät yleensä sisällä käyttöliittymätekstiä, mutta jos ne laukaisevat käyttöliittymäpäivityksiä, näiden päivitysten on oltava lokalisoituja. Tapahtumadatan tulisi olla ihanteellisesti kieliriippumatonta.
- Valuutat ja yksiköt: Jos tapahtumat sisältävät rahallisia arvoja tai mittoja, ole selkeä valuutan tai yksikön suhteen, tai suunnittele datan rakenne niin, että ne voidaan ottaa huomioon.
- Alueelliset säännökset (esim. GDPR, CCPA): Jos tapahtumat sisältävät henkilötietoja, varmista, että tapahtumaväylän toteutus ja siihen liittyvät micro-frontendit noudattavat asiaankuuluvia tietosuojasäännöksiä. Varmista, että dataa julkaistaan vain tilaajille, joilla on siihen laillinen tarve ja joilla on asianmukaiset suostumusmekanismit käytössä.
- Suorituskyky ja kaistanleveys: Käyttäjille alueilla, joilla on hitaammat internetyhteydet, vältä liian vilkkaita tapahtumakuvioita tai suuria tapahtumien datamääriä. Optimoi tiedonsiirto.
Yhteenveto
Frontend Micro-Frontend Event Bus on välttämätön malli, joka mahdollistaa saumattoman ja irrallisen viestinnän itsenäisten micro-frontend-sovellusten välillä. Omaksumalla julkaise-tilaa-mallin kehitystiimit voivat rakentaa monimutkaisia, skaalautuvia verkkosovelluksia säilyttäen samalla ketteryyden ja tiimien autonomian.
Valitsitpa sitten yksinkertaisen globaalin tapahtumien lähettäjän, hyödynnät mukautettuja DOM-tapahtumia tai integroit vankkoihin ulkoisiin viestinvälittäjiin, avainasemassa on selkeiden sopimusten määrittely, johdonmukaisten käytäntöjen luominen ja tapahtumakuuntelijoiden elinkaaren huolellinen hallinta. Hyvin toteutettu tapahtumaväylä muuttaa micro-frontendisi eristetyistä komponenteista yhtenäiseksi, dynaamiseksi ja reagoivaksi käyttäjäkokemukseksi.
Kun suunnittelet seuraavaa micro-frontend-hankettasi, muista priorisoida viestintästrategioita, jotka edistävät löyhää kytkentää ja skaalautuvuutta. Tapahtumaväylä, kun sitä käytetään harkitusti, on menestyksesi kulmakivi.